#include "../include/TimeWheel.h"
#include "../include/EventLoop.h"

TimerTask::TimerTask(uint64_t id, uint32_t delay, const TaskFunc &cb)
    : _id(id),
      _timeout(delay),
      _iscancel(false),
      _task_cb(cb)
{
}

void TimerTask::SetCancle() { _iscancel = true; }

bool TimerTask::getCancle() { return _iscancel; }

void TimerTask::SetRelease(ReleaseFunc rel) { _relfunc = rel; }

TimerTask::~TimerTask()
{
    // 定时器结束后就需要执行任务和释放连接
    // 定时器被取消说明这个任务不需要执行了，没被取消才说明是时间到了，该执行了
    if (_iscancel == false)
    {
        _task_cb();
    }
    // 无论是否取消，都调用释放函数（从哈希表删除）
    // 前提：TimerRefreshInLoop 中已经先删除了哈希表条目，所以这里不会误删新定时器
    _relfunc();
}

uint32_t TimerTask::getTimeOut() { return _timeout; }

const TaskFunc &TimerTask::getTaskFunc() const
{
    return _task_cb;
}

void TimeWheel::TimerRemove(uint64_t id)
{
    auto it = _timer.find(id);
    if (it != _timer.end())
        _timer.erase(id);
}

int TimeWheel::CreateTimerFd()
{
    int timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
    if (timerfd < 0)
    {
        ERR_LOG("Create timerfd failed");
        exit(static_cast<int>(TimeWheelExitCode::CREATE_TIMERFD_FAILED));
    }
    struct itimerspec start;
    start.it_interval.tv_sec = 1;
    start.it_interval.tv_nsec = 0;

    start.it_value.tv_sec = 1;
    start.it_value.tv_nsec = 0;
    timerfd_settime(timerfd, 0, &start, 0);

    return timerfd;
}

uint64_t TimeWheel::ReadTimerFd()
{
    uint64_t timers;
    ssize_t res = read(_timerfd, &timers, 8);
    if (res < 0)
    {
        ERR_LOG("Read TimerFd failed");
        exit(static_cast<int>(TimeWheelExitCode::READ_TIMERFD_FAILED));
    }
    return timers;
}

void TimeWheel::TimerAddInLoop(uint64_t id, uint32_t delay, const TaskFunc &tf)
{
    PtrTask pt(new TimerTask(id, delay, tf));
    pt->SetRelease(std::bind(&TimeWheel::TimerRemove, this, id));
    int pos = (_tick + delay) % _capacity;
    _wheel[pos].push_back(pt);
    _timer[id] = WeakTask(pt);
}

void TimeWheel::TimerRefershInLoop(uint64_t id)
{
    auto it = _timer.find(id);
    if (it == _timer.end())
        return;

    // 获取weakptr指向的shared_ptr资源
    PtrTask pt = it->second.lock();
    if (!pt)
        return;

    /**
     * 原来的逻辑是先取消定时任务，删除这个定时任务，然后创建一个新的任务，放到哈希表内，
     * 这个逻辑搭配现有的构造函数，就是错的，
     * 我的TimerCancelInLoop以及TimerCancel都没有对_cancel的状态进行修改，所以即使取消了任务，
     * 析构函数内的判断也一定会成立，这就是为什么即使一直在发送消息，时间一到，连接就关闭了
     * 我原来的逻辑删除后就立即调用析构了，所以连接就挂断了
     */
    // 刷新定时器：直接把同一个定时任务重新添加到时间轮的新位置
    // 不需要创建新的TimerTask对象，也不需要取消旧的
    int delay = pt->getTimeOut();
    int pos = (_tick + delay) % _capacity;
    _wheel[pos].push_back(pt);
}

void TimeWheel::TimerCancelInLopp(uint64_t id)
{
    auto it = _timer.find(id);
    if (it == _timer.end())
        return;

    PtrTask pt = it->second.lock();
    if (pt)
        pt->SetCancle();
}

void TimeWheel::OnTime()
{
    uint64_t ret = ReadTimerFd();
    for (int i = 0; i < ret; i++)
        RunTimerTask();
}

TimeWheel::TimeWheel(EventLoop *loop)
    : _tick(0),
      _wheel(_capacity),
      _loop(loop),
      _timerfd(CreateTimerFd()),
      _timer_channel(new Channel(loop, _timerfd))
{
    _timer_channel->SetReadCallBack(std::bind(&TimeWheel::OnTime, this));
    _timer_channel->EnableRead();
}

// 哈希表的插入在多线程中可能会出问题，这里调用EventLoop的RunInLoop方法，这个方法内部加了锁，是线程安全的
void TimeWheel::TimerAdd(uint64_t id, uint32_t delay, const TaskFunc &tf) { _loop->RunInLoop(std::bind(&TimeWheel::TimerAddInLoop, this, id, delay, tf)); }

void TimeWheel::TimerRefersh(uint64_t id) { _loop->RunInLoop(std::bind(&TimeWheel::TimerRefershInLoop, this, id)); }

void TimeWheel::TimerCancel(uint64_t id) { _loop->RunInLoop(std::bind(&TimeWheel::TimerCancelInLopp, this, id)); }

void TimeWheel::RunTimerTask()
{
    _tick = (_tick + 1) % _capacity;
    _wheel[_tick].clear();
}

bool TimeWheel::HasTimer(uint64_t id)
{
    return _timer.find(id) != _timer.end();
}

TimeWheel::~TimeWheel()
{
    ::close(_timerfd);
    // delete _loop;
}
